
package net.sourceforge.jasonide.wizards;

import jason.mas2j.MAS2JProject;

import java.io.IOException;

import org.eclipse.core.internal.resources.File;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jface.dialogs.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.*;

/**
 * The "New" wizard page allows setting the container for the new file as well as the file name. The page will only
 * accept file name without the extension OR with the extension that matches the expected one (java).
 * 
 * @author Daniel Nicholas Kiss
 */
public class NewModuleWizardPage extends WizardPage {
	private Text containerText;
	private Text agentText;
	private Text moduleText;
	private Text sourceText;
	
	private String containerName = null;
	
	/**
	 * Constructor for SampleNewWizardPage.
	 * 
	 * @param pageName
	 */
	public NewModuleWizardPage(ISelection selection) {
		super("wizardPage");
		
		if (selection != null && !selection.isEmpty()) {
			if (selection instanceof ITreeSelection) {
				ITreeSelection ts = (ITreeSelection)selection;
				
				Object firstElement = ts.getFirstElement();
				if (firstElement != null) {
					if (firstElement instanceof File) {
						File f = (File)firstElement;
						containerName = f.getProject().getName() + "/src/asl";
					} else
					// is a Java Project
					if (firstElement instanceof IJavaProject) {
						IJavaProject pfr = (IJavaProject)firstElement;
						containerName = pfr.getProject().getName() + "/src/asl";
					}
					// is a Project
					else if (firstElement instanceof IProject) {
						IProject pfr = (IProject)firstElement;
						containerName = pfr.getName() + "/src/asl";
					}
					// is it an element of a Project
					else if (firstElement instanceof IJavaElement) {
						IJavaElement parent = (IJavaElement)firstElement;
						while (parent != null && !(parent instanceof IJavaProject)) {
							parent = parent.getParent();
						}
						if (parent != null && parent instanceof IJavaProject) {
							containerName = ((IJavaProject)parent).getProject().getName() + "/src/asl";
						}
					}
				}
			} else if (selection instanceof StructuredSelection) {
				Object fe = ((StructuredSelection)selection).getFirstElement();
				if (fe instanceof File) {
					File f = (File)fe;
					containerName = f.getProject().getName() + "/src/asl";
				} else {
					IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
					IProject[] projects = root.getProjects();
					for(int i = 0; i < projects.length; i++) {
						if (projects[i].isOpen() && projects[i].getFolder("src/asl").exists()) {
							containerName = projects[i].getName() + "/src/asl";
							break;
						}
					}
				}
			}
		}
		
		setTitle("New Module");
		setDescription("This wizard creates a new Module.");
	}
	
	/**
	 * @see IDialogPage#createControl(Composite)
	 */
	public void createControl(Composite parent) {
		Composite container = new Composite(parent, SWT.NULL);
		GridLayout layout = new GridLayout();
		container.setLayout(layout);
		layout.numColumns = 3;
		layout.verticalSpacing = 9;
		
		Label label = new Label(container, SWT.NULL);
		label.setText("Source &Folder:");
		
		containerText = new Text(container, SWT.BORDER | SWT.SINGLE);
		GridData gd = new GridData(GridData.FILL_HORIZONTAL);
		containerText.setLayoutData(gd);
		containerText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				dialogChanged();
			}
		});
		
		Button button = new Button(container, SWT.PUSH);
		button.setText("Browse...");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleBrowseSourceFolder();
			}
		});
		
		label = new Label(container, SWT.NULL);
		label.setText("&Module Name:");
		
		moduleText = new Text(container, SWT.BORDER | SWT.SINGLE);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		moduleText.setLayoutData(gd);
		moduleText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				dialogChanged();
			}
		});
		
		label = new Label(container, SWT.NULL);
		
		label = new Label(container, SWT.NULL);
		label.setText("&Agent Source:");
		
		agentText = new Text(container, SWT.BORDER | SWT.SINGLE);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		agentText.setLayoutData(gd);
		agentText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				dialogChanged();
			}
		});
		
		button = new Button(container, SWT.PUSH);
		button.setText("Browse...");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleBrowseAgentSource();
			}
		});
		
		label = new Label(container, SWT.NULL);
		label.setText("Module &Source:");
		
		sourceText = new Text(container, SWT.BORDER | SWT.SINGLE);
		gd = new GridData(GridData.FILL_HORIZONTAL);
		sourceText.setLayoutData(gd);
		sourceText.addModifyListener(new ModifyListener() {
			public void modifyText(ModifyEvent e) {
				dialogChanged();
			}
		});
		
		button = new Button(container, SWT.PUSH);
		button.setText("Browse...");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				handleBrowseModuleSource();
			}
		});
		
		dialogChanged();
		setControl(container);
		
		if (containerName != null && !containerName.isEmpty()) {
			containerText.setText(containerName);
			moduleText.setFocus();
		} else {
			containerText.setFocus();
		}
	}
	
	/**
	 * Uses the standard container selection dialog to choose the new value for the container field.
	 */
	private void handleBrowseSourceFolder() {
		ContainerSelectionDialog dialog = new ContainerSelectionDialog(getShell(), ResourcesPlugin.getWorkspace().getRoot(), false, "Select new source folder");
		if (dialog.open() == ContainerSelectionDialog.OK) {
			Object[] result = dialog.getResult();
			if (result.length == 1) {
				containerText.setText(((Path)result[0]).toString());
			}
		}
	}
	
	private void handleBrowseAgentSource() {
		FileDialog dialog = new FileDialog(getShell());
		dialog.setFilterExtensions(new String[] {"*.asl;*.aslm", "*.*"});
		dialog.setFilterNames(new String[] {"AgentSpeak Language files (*.asl;*.aslm)", "All files (*.*)"});
		String result = dialog.open();
		if (result != null) {
			agentText.setText(result);
		}
	}
	
	private void handleBrowseModuleSource() {
		FileDialog dialog = new FileDialog(getShell());
		dialog.setFilterExtensions(new String[] {"*.asl;*.aslm", "*.*"});
		dialog.setFilterNames(new String[] {"AgentSpeak Language files (*.asl;*.aslm)", "All files (*.*)"});
		String result = dialog.open();
		if (result != null) {
			sourceText.setText(result);
		}
	}
	
	/**
	 * Ensures that both text fields are set.
	 */
	private void dialogChanged() {
		try {
			IResource container = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(getContainerName()));
			String moduleName = getModuleName();
			if (moduleName == null) moduleName = "";
			String moduleSource = getModuleSource();
			if (moduleSource == null) moduleSource = "";
			
			if (getContainerName().length() == 0) {
				updateStatus("Source folder must be specified");
				return;
			}
			if (container == null || (container.getType() & (IResource.PROJECT | IResource.FOLDER)) == 0) {
				updateStatus("Source folder must exist");
				return;
			}
			if (!container.isAccessible()) {
				updateStatus("Source folder must be writable");
				return;
			}
			
			if (moduleName.isEmpty() && moduleSource.isEmpty()) {
				updateStatus("At least either the name of the module or its source has to be specified");
				return;
			}
			if (!moduleName.isEmpty() && Character.isUpperCase(moduleName.charAt(0))) {
				updateStatus("The name of the module has to start with a lowercase letter");
			}
			
			IResource agentSource = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(getAgentSource()));
			if (!getAgentSource().isEmpty()) {
				if (agentSource != null && !agentSource.isAccessible()) {
					updateStatus("Agent source must be writable");
					return;
				}
				if (agentSource == null || (agentSource.getType() & IResource.FILE) == 0) {
					setErrorMessage(null);
					setMessage("Agent source does not exists within project: " + getAgentSource(), WARNING);
					setPageComplete(true);
					return;
				}
			}
			
			updateStatus(null);
		} catch (Exception e) {
			MessageDialog.openError(getShell(), "Error", e.toString());
		}
	}
	
	private void updateStatus(String message) {
		setErrorMessage(message);
		setMessage(null);
		setPageComplete(message == null);
	}
	
	public String getContainerName() {
		return containerText.getText();
	}
	
	public String getModuleName() {
		return moduleText.getText();
	}
	
	public String getAgentSource() {
		String agent = agentText.getText();
		if (agent.isEmpty()) return agent;
		
		if (agent.indexOf('.') == -1) {
			agent += '.' + MAS2JProject.AS_EXT;
		}
		
		Path agentSourcePath = new Path(agent);
		if (agentSourcePath.isAbsolute()) {
			IResource container = ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(getContainerName()));
			if (container == null) return agent;
			String localPath;
			String dir;
			try {
				localPath = agentSourcePath.toFile().getCanonicalPath();
				dir = container.getProject().getLocation().toFile().getParentFile().getCanonicalPath();
			} catch (IOException e) {
				localPath = agentSourcePath.toFile().getAbsolutePath();
				dir = container.getProject().getLocation().toFile().getParentFile().getAbsolutePath();
			}
			
			int dirPos = localPath.toLowerCase().indexOf(dir.toLowerCase());
			if (dirPos != -1) localPath = localPath.substring(dirPos + dir.length());
			
			localPath.replace(java.io.File.separatorChar, '/');
			
			return localPath;
		} else if (agent.startsWith("/")) {
			return agent;
		} else {
			return getContainerName() + '/' + agent;
		}
	}
	
	public String getModuleSource() {
		return sourceText.getText();
	}
}
